/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.ufpb.halloc.dados;

import br.com.ufpb.halloc.exceptions.ArquivoBinInexistenteException;
import br.com.ufpb.halloc.exceptions.DisciplinaDuplicadaException;
import br.com.ufpb.halloc.exceptions.DisciplinaInexistenteException;
import br.com.ufpb.halloc.exceptions.DisciplinaInvalidaException;
import br.com.ufpb.halloc.exceptions.DisciplinasVaziaException;
import br.com.ufpb.halloc.negocio.Disciplina;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.JOptionPane;

/**
 *
 * @author leonardo
 */
public class DisciplinaDAOArquivoBin implements DisciplinaDAO{

    
    private ObjectInputStream ins;
    private ObjectOutputStream outs;
    private final String FILENAME = "disciplinas.bin";
    private File arquivo;
    
    
    /* Mapa das disciplinas cadastradas neste arquvo */
    Map<String, Disciplina> mapDiscBin;
    
    
    /* SINGLETON */
    private static DisciplinaDAOArquivoBin daoArquivoBin;  
    
    public static DisciplinaDAOArquivoBin getInstance(){
        if (daoArquivoBin == null)
            daoArquivoBin = new DisciplinaDAOArquivoBin();
        return daoArquivoBin;
    }
    
    
    
    /* COSTRUTOR */
    private DisciplinaDAOArquivoBin() {

        this.arquivo = new File(FILENAME);
        this.mapDiscBin = new HashMap<String, Disciplina>();
        
    }    
    
    
    
    /**
     * Salva todas as disciplinas que estão mantidas por esta classe e que
     * pertencem a estrutura de arquivo binário.
     * @exception IOException
     */
    public void salvaDisciplinasNoArquivoBin() {        
        try {
            outs = new ObjectOutputStream(new FileOutputStream(this.arquivo));
            outs.writeObject(this.mapDiscBin);
            outs.flush();
            outs.close();
        } catch (IOException ex){ ex.printStackTrace(); }
    }
    
    
    
    /**
     * Recupera uma lista com todas as disciplinas cadastradas em um
     * arquivo binário.
     * @exception IOException, ClassNotFoundException.
     */
    public void recuperaDisciplinasDoArquivoBin(){
        
        try{
            ins = new ObjectInputStream(new FileInputStream(arquivo));
            mapDiscBin = (Map<String, Disciplina>) ins.readObject();
            ins.close();       
        } 
        catch (IOException ex){ ex.printStackTrace(); } 
        catch (ClassNotFoundException ex) { ex.printStackTrace(); }
        
    }
    
    
    
    /**
     * Remove o arquivo binário das Disciplinas.
     */
    public void removerArquivoBin() throws ArquivoBinInexistenteException {
        File file = new File(this.FILENAME);
        if(file.exists()){
            file.delete();
            this.mapDiscBin.clear();
            return;
        }
        throw new ArquivoBinInexistenteException("#ERRO! Ocorreu um erro ao tentar remover o arquivo binário\n"
                                                   + "das disciplinas. Verifique e tente novamente.");
    }
    
    
    
    /**
     * Cria o arquivo binário das Disciplinas.
     */
    public void criarArquivoBin() throws ArquivoBinInexistenteException {
        try {
            File file = new File(this.FILENAME);
            if (!file.exists()) 
                file.createNewFile();
            
        } catch (IOException ex) {
            throw new ArquivoBinInexistenteException("#ERRO! Ocorreu um erro ao tentar criar o arquivo binário\n"
                                                   + "das disciplinas. Verifique a descrição do erro abaixo e \n"
                                                   + "tente novamente. \nDescrição:\n" + ex.getStackTrace() );
        }
    }
    
    
    
    /**
     * Insere uma nova Disciplina na estrutura utilizada por esta classe
     * para guardar em uma estrutura de mapa.
     * @param Disciplina
     * @return boolean - true, se a inserção foi realizada com êxito, e
     * false caso contrário.
     */
    @Override
    public boolean inserir(Disciplina disc) throws DisciplinaInvalidaException, DisciplinaDuplicadaException {
        
        if (disc != null) {
            if (!mapDiscBin.containsKey(disc.getID())) {
                mapDiscBin.put(disc.getID(), disc);
                return true;
            }
            throw new DisciplinaDuplicadaException("Ops! Essa disciplina já está cadastrada.");
        }
        throw new DisciplinaInvalidaException("Ocorreu um erro ao tentar cadastrar a disciplina.\n"
                + "Verifique e tente novamente.");
    }

    
    
    /**
     * Remove uma determinada disciplina deste arquivo binário.
     * 
     * @param Disciplina - disciplina a ser removida.
     * @return boolean - true se remover corretamente.
     * @exception DisciplinaInexistenteException, DisciplinaInvalidaException
     */
    @Override
    public boolean remover(Disciplina disc) throws DisciplinaInexistenteException, DisciplinaInvalidaException,
            DisciplinasVaziaException {
        
        if (!this.mapDiscBin.isEmpty()) {
            if (disc != null) {
                if (mapDiscBin.containsValue(disc)) {
                    this.mapDiscBin.remove(disc.getID());
                    return true;
                } else {
                    throw new DisciplinaInexistenteException("Ops! A disciplina a ser removida não existe.");
                }
            }
            throw new DisciplinaInvalidaException("Ocorreu um erro ao tentar remover a disciplina.\n"
                    + "Verifique e tente novamente.");
        }
        throw new DisciplinasVaziaException("Opa!!! Não existem disciplinas cadastradas nesse arquivo binário.");
    
    }

    
    
    /**
     * Pesquisa por uma determinada disciplina através do seu ID (identificação).
     * 
     * @param String id - identificação desta disciplina
     * @return Disciplina - uma disciplina caso esteja cadastrada.
     * @exception DisciplinaInexistenteException, DisciplinaInvalidaException
     */
    @Override
    public Disciplina pesquisarID(String id) throws DisciplinaInexistenteException, DisciplinaInvalidaException{
        if (id != null && !id.equals("")){
            if (mapDiscBin.containsKey(id))
                return mapDiscBin.get(id);
            else
                throw new DisciplinaInexistenteException("Ops! A Disciplina " + id + " é inexistente.");
        }
        throw new DisciplinaInvalidaException("Erro! Parâmetros de pesquisa inválido(s).");
    }
    
    

    /**
     * Pesquisa por uma determinada disciplina através do seu nome.
     * 
     * @param String nome - nome correspondente a disciplina
     * @return Disciplina - uma disciplina caso esteja cadastrada.
     * @exception DisciplinaInexistenteException - caso a disciplina
     * pesquisada não esteja cadastrada neste arquivo binário.
     */
    @Override
    public List<Disciplina> pesquisar(String nome)throws DisciplinaInvalidaException {
        List<Disciplina> list = new ArrayList<Disciplina>();
        if (nome != null && !nome.equals("")){
            Iterator<Disciplina> i = this.mapDiscBin.values().iterator();
            while(i.hasNext()){
                Disciplina d = (Disciplina) i.next();
                if (d.getNome().toUpperCase().startsWith(nome.toUpperCase())){
                    list.add(d);
                }
            }
            return list;
        }
        throw new DisciplinaInvalidaException("Erro! Parâmetros de pesquisa inválido(s).");
    }    


    /**
     * Recebe uma Disciplina atualizada (com exceção do seu ID) e verifica
     * se a mesma está cadastrada neste arquivo e então, basicamente
     * remove toda a instancia anterior através do ID, e então cadastra a
     * nova, já com as modificações.
     * 
     * @param Disciplina - disciplina atualizada
     * @exception DisciplinaInexistenteException, DisciplinaInvalidaException
     * @return boolean - true se atualizar com sucesso
     * 
     */
    @Override
    public boolean atualizar(Disciplina disc) throws DisciplinaInexistenteException, DisciplinasVaziaException{
        if (!this.mapDiscBin.isEmpty()) {
            if (disc != null) {
                if (mapDiscBin.containsKey(disc.getID())) {
                    mapDiscBin.remove(disc.getID());
                    mapDiscBin.put(disc.getID(), disc);
                    return true;
                } else {
                    throw new DisciplinaInexistenteException("Ops! A disciplina a ser atualizada não existe.");
                }
            }
            throw new DisciplinaInvalidaException("Ocorreu um erro ao tentar remover a disciplina.\n"
                    + "Verifique e tente novamente.");
        }
        throw new DisciplinasVaziaException("Opa!!! Não existem disciplinas cadastradas nesse arquivo binário.");
    }
    
    
    
    /**
     * Retorna a quantidade de Disciplinas que estão cadastradas neste arquivo binário.
     * @return int - indicando a quantidade de disciplinas
     */
    public int getSize(){
        return this.mapDiscBin.size();
    }

    public List<Disciplina> getDisciplinas() {
        List<Disciplina> lista = new ArrayList<Disciplina>();
        Iterator<Disciplina> i = this.mapDiscBin.values().iterator();
        while(i.hasNext()){
            Disciplina d = (Disciplina) i.next();
            lista.add(d);            
        }
        return lista;
    }
    
}
